package com.xhtt.fzxy.console;

import net.lastcoder.ifserver.util.TextUtils;

import org.apache.mina.util.ConcurrentHashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.Set;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**会话鉴权
 * auth: WenYF
 * date: 2018/5/5
 */
public class AuthFilter implements Filter {
    public static final Logger Log = LoggerFactory.getLogger(AuthFilter.class);

    private AdminManager manager = AdminManager.getInstance();

    private static Set<String> excludes = new ConcurrentHashSet<>();

    private ServletContext context;
    private String defaultLoginPage = "login.html";

    public static void addExclude(String exclude) {
        excludes.add(exclude);
    }

    public static void removeExclude(String exclude) {
        excludes.remove(exclude);
    }

    /**
     * @param url
     * @param exclude
     * @return
     */
    public static boolean testURLPassesExclude(String url, String exclude) {
        if (url.startsWith("assets")) {
            if (!url.contains("..") && !url.toLowerCase().contains("%2e")) {
                return true;
            }
        }
        else if (exclude.endsWith("*")) {
            if (url.startsWith(exclude.substring(0, exclude.length()-1))) {
                if (!url.contains("..") && !url.toLowerCase().contains("%2e")) {
                    return true;
                }
            }
        }
        else if (exclude.contains("?")) {
            if (url.equals(exclude)) {
                return true;
            }
        }
        else {
            int paramIndex = url.indexOf("?");
            if (paramIndex != -1) {
                url = url.substring(0, paramIndex);
            }
            if (url.equals(exclude)) {
                return true;
            }
        }
        return false;
    }

    public void init(FilterConfig config) throws ServletException {
        context = config.getServletContext();
        excludes.add("fzxy/login");
        excludes.add("login.*");
        excludes.add("error.*");
    }

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        String loginPage = defaultLoginPage;
        String url = request.getRequestURI().substring(1);
        boolean doExclude = false;
        for (String exclude : excludes) {
            if (testURLPassesExclude(url, exclude)) {
                doExclude = true;
                break;
            }
        }

        HttpSession session = request.getSession();
        System.out.println(((HttpServletRequest) req).getRequestURL().toString() + "," + session.getId());
        WebConsolePlugin.Log.info(((HttpServletRequest) req).getRequestURL().toString() + "," + session.getId());
        if (!doExclude) {
            Admin admin = manager.getAdmin(request, response, request.getSession());
            if (admin == null) {
                response.sendRedirect(getRedirectURL(request, loginPage, 1, null));
                return;
            } else {
                // 重置到期时间
                admin.startTime = System.currentTimeMillis();
                
                // 检测权限
                if (!checkLevel(request, response, admin, url)) {
                    return;
                }
            }
        }
        
        chain.doFilter(req, res);
    }

    /**
     * @param req
     * @param res
     * @param admin
     * @param url
     * @return  true继续往下，false停止往下
     */
    private boolean checkLevel(HttpServletRequest req, HttpServletResponse res, Admin admin, String url) throws IOException {
        if (TextUtils.isEmpty(url)) {
            return true;
        }
        String[] splits = url.split("/");
        
        if (splits.length < 2) {
            return true;
        }
        
        if (!splits[0].equals("console")) {
            return true;
        }
        
        for (int i = 0; i <= 4; i++) {
            if (splits[1].equals("l" + i) && admin.level > i) {
                res.sendRedirect(getRedirectURL(req, "forbidden.html", splits.length - 2, null));
                return false;
            }
        }
        
        return true;
    }
    
    public void destroy() {
    }

    /**
     * @param request
     * @param page
     * @param pathLength 前面加几个"../"
     * @param optionalParams
     * @return
     */
    private String getRedirectURL(HttpServletRequest request, String page,
                                  int pathLength, String optionalParams)
    {
        StringBuilder buf = new StringBuilder();
        int splitsCount = 0;
        try {
            String uri = request.getRequestURI();
            for (char c : uri.toCharArray()) {
                if (c == '/') {
                    splitsCount++;
                }
            }
            buf.append(uri);
            String qs = request.getQueryString();
            if (qs != null) {
                buf.append("?").append(qs);
            }
        }
        catch (Exception e) {
            Log.error(e.getMessage(), e);
        }
        try {
            String tag = "";
            for (int i = (splitsCount - pathLength); i < splitsCount && splitsCount > 1; i++) {
                tag = tag + "../";
            }
            
            return tag + page + "?url=" + URLEncoder.encode(buf.toString(), "UTF-8")
                    + (optionalParams != null ? "&"+optionalParams : "");
        }
        catch (Exception e) {
            Log.error(e.getMessage(), e);
            return null;
        }
    }
}
